/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.io.metacyc;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;

import combi.sipper.io.IODistanceMatrix;
import combi.sipper.io.IOSimpleGraph;
import combi.sipper.modeling.distances.DistanceBetweenGenes;
import combi.sipper.modeling.distances.DistanceCoexpression;
import combi.sipper.modeling.distances.DistanceColocalisation;
import combi.wip.sipper.modeling.Protein;
import combi.wip.sipper.modeling.integratedModel.IntegratedModel;
import combi.wip.sipper.io.IOGenome;
import combi.wip.sipper.io.IOIntegratedModel;
import combi.wip.sipper.io.IOMetabolicNetworkWithEnzymes;
import combi.wip.sipper.io.Profil;
import combi.wip.sipper.modeling.genomes.Gene;
import combi.wip.sipper.modeling.genomes.Genome;
import combi.wip.sipper.modeling.integratedModel.GenomicModel;
import combi.wip.sipper.modeling.metabolicNetworks.ImplOfFilteredMetabolicNetworkWithEnzymes;
import combi.wip.sipper.modeling.metabolicNetworks.MetabolicNetworkWithEnzymes;
import combi.wip.sipper.modeling.reactions.Reaction;

public class IOIntegratedModelFromMetaCyc extends IOIntegratedModel<Gene<Protein>, String, Reaction<String>,Protein>{
	//<Gene<String>, String, Reaction<String>,String> 

	protected IntegratedModel<Gene<Protein>, String, Reaction<String>, Protein> model;

	protected String fileGenome;
	protected String fileCompounds;
	protected String fileReactions;
	protected String fileEnzReactions;
	protected String fileProteins;
	protected String fileDistance;

	protected MetabolicNetworkWithEnzymes<String,Reaction<String>,Protein> meta;
	protected Genome<Gene<Protein>,Protein> genome;
	protected boolean circular;
	protected DistanceBetweenGenes<Gene<Protein>> distance;

	public IOIntegratedModelFromMetaCyc(String fileGenome, boolean isCircular, String fileCompounds, String fileReactions, String fileEnzReactions, String fileProteins)
	{
		this.fileGenome = fileGenome;
		this.fileCompounds = fileCompounds;
		this.fileReactions = fileReactions;
		this.fileEnzReactions = fileEnzReactions;
		this.fileProteins = fileProteins;
		this.fileDistance = null;
	}

	public IOIntegratedModelFromMetaCyc(String fileGenome, boolean isCircular, String fileDistance, String fileCompounds, String fileReactions, String fileEnzReactions, String fileProteins)
	{
		this.fileGenome = fileGenome;
		this.fileCompounds = fileCompounds;
		this.fileReactions = fileReactions;
		this.fileEnzReactions = fileEnzReactions;
		this.fileDistance = fileDistance;
		this.fileProteins = fileProteins;
	}

	public void load() throws IOException
	{
		IOGenome<Gene<Protein>, Protein> genomeIO = new IOGenomeFromMetaCyc<Gene<Protein>>(fileGenome, circular, fileProteins);
		IOMetabolicNetworkWithEnzymes<String, Reaction<String>, Protein> metaIO = new IOMetabolicNetworkWithEnzymesFromMetaCyc(this.fileCompounds,this.fileReactions,this.fileEnzReactions, this.fileProteins);
		genomeIO.load();
		metaIO.load();
		genome = genomeIO.get();
		meta = metaIO.get();
		if (fileDistance==null)
		{
			distance=new DistanceColocalisation<Gene<Protein>, Protein>(genome);
		}
		else
		{
			IODistanceMatrix matrix = IODistanceMatrix.load(fileDistance);
			List<Gene<Protein>> matrixGenes = adjustGenesIDs(genome, matrix.getIDs());//ajust the genes ID of the matrix according to the ids stored in the genome
			distance=new DistanceCoexpression<Gene<Protein>, Protein>(genome, matrix.getMatrix(), matrixGenes);
		}
		model = new GenomicModel<Gene<Protein>, String, Reaction<String>, Protein>(meta, genome, distance);
	}

	private  List<Gene<Protein>> adjustGenesIDs(Genome<Gene<Protein>, Protein> genome,
			List<String> iDs) {
		List<Gene<Protein>> result = new ArrayList<Gene<Protein>>(iDs.size());
		for(String id : iDs)
		{
			Gene<Protein> g = genome.getGene(id);
			if (g == null){
				g = new Gene<Protein>(id);
			}
			result.add(g);
		}
		return result;
	}

	public IntegratedModel<Gene<Protein>, String, Reaction<String>, Protein> get()
	{
		return model;
	}

	public static void main(String[] args)
	{
		int filter = 80;
		String profilFile = args[0];
		String outModel = args[1];
		String outbioASPGenome = args[2];
		String outbioASPMetabolism = args[3];
		String outbioASPAsso = args[4];
		try {
			Profil ecocycFiles = Profil.createProfil(profilFile);
			IOIntegratedModel<Gene<Protein>, String, Reaction<String>, Protein> modelIn;
			modelIn = new IOIntegratedModelFromMetaCyc(ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("genome"), true,
					ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("compounds"),
					ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("reactions"),
					ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("enzrxns"),
					ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("proteins"));

			long oldtime = System.currentTimeMillis();
			System.out.println("Loading data from the files :\n" + ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("compounds")
					+ "\n" + ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("reactions")
					+ "\n" + ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("enzrxns")
					+ "\n" + ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("genome")
					+ "\n" + ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("proteins"));
			modelIn.load();
			long newtime = System.currentTimeMillis();
			System.out.println("the process tooks " + (newtime - oldtime) + "ms");
			
			System.out.println(modelIn.get().getMetabolism().getCompoundSet().size() + "compounds");
			System.out.println(modelIn.get().getReactionSet().size() + "reactions");
			System.out.println(modelIn.get().getEnzymeSet().size() + "enzymes");
			System.out.println(modelIn.get().getGenome().size() + "genes");
			
			
			
			
			oldtime = System.currentTimeMillis();
			
			System.out.println("Generating filtered metabolic network with <= "+ filter+" compounds");
			oldtime = System.currentTimeMillis();
			combi.wip.sipper.modeling.metabolicNetworks.MetabolicNetworkUtils<String, Reaction<String>> utils = 
					new combi.wip.sipper.modeling.metabolicNetworks.MetabolicNetworkUtils<String, Reaction<String>>();

			Collection<String> filterCPD = new HashSet<String>();
			for(Entry<String, Integer> entry : utils.compoundsCount(modelIn.get().getMetabolism()).entrySet())
			{
				if (entry.getValue()>filter)
				{
					filterCPD.add(entry.getKey());
				}
			}
			System.out.println(filterCPD.size() + "compounds to be filtered\n" 
					+ filterCPD);
			
			GenomicModel<Gene<Protein>, String, Reaction<String>, Protein> newModel = 
					new GenomicModel<Gene<Protein>, String, Reaction<String>, Protein>(
							new ImplOfFilteredMetabolicNetworkWithEnzymes<String, Reaction<String>, Protein>(modelIn.get().getMetabolism(),filterCPD),
							modelIn.get().getGenome(),
							((GenomicModel)modelIn.get()).getDistance());
			
			newtime = System.currentTimeMillis();
			System.out.println("the process tooks " + (newtime - oldtime) + "ms");

			oldtime = System.currentTimeMillis();

			System.out.println("Generating integrated graph");
			combi.wip.sipper.modeling.integratedModel.graphs.GraphGenomicModel<Gene<Protein>, String, Reaction<String>, Protein> graphModel;
			graphModel = new combi.wip.sipper.modeling.integratedModel.graphs.GraphGenomicModel<Gene<Protein>, String, Reaction<String>, Protein>((GenomicModel<Gene<Protein>, String, Reaction<String>, Protein>) newModel);
			graphModel.generateGraph();
			IOSimpleGraph.export(graphModel.getGraph(),outModel);

			newtime = System.currentTimeMillis();
			System.out.println("the process tooks " + (newtime - oldtime) + "ms");
			System.out.println("Number of vertices : "+ graphModel.getGraph().vertexSet().size());
			System.out.println("Number of edges : "+ graphModel.getGraph().edgeSet().size());

			oldtime = System.currentTimeMillis();
			
			System.out.println("Export bioASP integrated graph");
			combi.wip.sipper.io.IObioASP export = new combi.wip.sipper.io.IObioASP();
			export.export(newModel,outbioASPGenome, outbioASPMetabolism, outbioASPAsso);
			newtime = System.currentTimeMillis();
			System.out.println("the process tooks " + (newtime - oldtime) + "ms");
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
